﻿#include "../../ctlEdit.h"
#include "../../ctlHelp.h"
#include "../../edit/clipboard.h"
#include <algorithm>

#define CARET_SELECT (_edit_caret_pos_begin.section != _edit_caret_pos_end.section || \
	_edit_caret_pos_begin.offset != _edit_caret_pos_end.offset || \
	_edit_caret_pos_begin.line != _edit_caret_pos_end.line)

#define CARET_COMP_V(beg, end, op) \
	(beg.section op end.section || \
	(beg.section == end.section && beg.line op end.line) || \
	(beg.section == end.section && beg.line == end.line && \
	beg.offset op end.offset))

#define CARET_COMP(op) \
	CARET_COMP_V(_edit_caret_pos_begin, _edit_caret_pos_end, op)

#define EDIT_CHECK_MARCO\
	(unsigned)getWidth() < getRightPadding() + getLeftPadding() + getFont()->getMaxCharWidth()\
			|| (unsigned)getHeight() + 5 < getHeightPadding() * 2

namespace swui
{
	unsigned ctlEdit::getParagraphLineNumber(unsigned number)
	{
		return _getLineNumber(number);
	}
	bool ctlEdit::removeParagraph(unsigned number)
	{
		if(number >= getParagraphNumber())
			return false;
		_edit_caret_pos_begin = _exactPos(number, 0, 0);
		_edit_caret_pos_end = _exactPos(number, 
			_getLineNumber(number) - 1,
			_getSection(number)->wrap.back().second - 
			_getSection(number)->wrap.back().first);
		_exactPos eps = _get_previous_pos(_edit_caret_pos_begin);
		if(eps.section == _edit_caret_pos_begin.section)
			_edit_caret_pos_end = _get_next_pos(_edit_caret_pos_end);
		else _edit_caret_pos_begin = eps;
		_remove_selected();
		_send_change_event();
		redraw();
		return true;
	}
	bool ctlEdit::insertParagraph(unsigned number, const ctlText& text)
	{
		if(number > getParagraphNumber())
			return false;
		_edit_str.insert(_edit_str.begin() + number, _textInfo());
		bool result = setParagraph(number, text);
		_exactPos eps = _get_previous_pos(_edit_back_remove.begin);
		if(eps.section == _edit_back_remove.begin.section)
			_edit_back_remove.end = _get_next_pos(_edit_back_remove.end);
		else _edit_back_remove.begin = eps;
		return result;
	}
	bool ctlEdit::setParagraph(unsigned number, const ctlText& text)
	{
		if(number >= getParagraphNumber())
			return false;
		_edit_back_insert.enable = true;
		_edit_back_insert.pack = std::move(_edit_str[number].str);
		_edit_back_insert.begin = _exactPos(number, 0, 0);
		_edit_str[number].str.set(text);
		std::wstring& wr = _edit_str[number].str.stdwstr();
		wr.erase(wr.begin() + wr.find('\n'), wr.end());
		_calc_text_rect(_edit_pos(number, 0));
		_edit_back_remove.enable = true;
		_edit_back_remove.begin = _exactPos(number, 0, 0);
		_edit_caret_pos_begin = _edit_caret_pos_end =
			_edit_back_remove.end = _exactPos(number, 
			_getLineNumber(number) - 1,
			_getSection(number)->wrap.back().second - 
			_getSection(number)->wrap.back().first);
		_send_change_event();
		redraw();
		return true;
	}
	std::string ctlEdit::getParagraph(unsigned number)
	{
		if(number >= getParagraphNumber())
			return std::string();
		return _edit_str[number].str.getstr();
	}
	std::wstring ctlEdit::getWideParagraph(unsigned number)
	{
		if(number >= getParagraphNumber())
			return std::wstring();
		return _edit_str[number].str.getwidestr();
	}
	unsigned ctlEdit::getParagraphNumber()
	{
		return _edit_str.size();
	}
	bool ctlIsBlank(std::wstring::value_type value)
	{
		return value == L' ' || value == L'\t';
	}
	bool ctlIsAlnum(std::wstring::value_type value)
	{
		return value >= L'0' && value <= L'9' ||
			value >= L'a' && value <= L'z' ||
			value >= L'A' && value <= L'Z' || 
			value == L'_';
	}
	static const unsigned ScrollStepPixel = 2;
	void ctlEdit::_flush_font_()
	{
		IFont::_flush_font_();
		_edit_rect_recalc();
	}
	void ctlEdit::_calc_text_rect(_edit_pos beg)
	{
		if(EDIT_CHECK_MARCO) return;
		unsigned lineWidth = getWidth() - (getRightPadding() + getLeftPadding());
		//获取指定的段
		auto& section = _edit_str[beg.first];
		//获取每一行的数据
		std::vector<_edit_pos>& sectionData = section.wrap;
		auto& text = section.str;
		sectionData.push_back(_edit_pos(0, 0));
		auto lineIter = sectionData.begin() + beg.second;
		//删除多余的部分
		sectionData.erase(lineIter + 1, sectionData.end());
		lineIter = sectionData.end() - 1;
		unsigned textlen = section.str.length();
		//计算一行可能容纳的最大字符数（不精确）
		unsigned advLineCharNumber = getWidth() * 7 / getFont()->getAveCharWidth();
		unsigned charlLength = getFont()->getTextSize(L"l").x;
		advLineCharNumber = ctlMax(advLineCharNumber >> 1, 
			getWidth() * 2 / charlLength);
		for(; ;) {
			unsigned pos = getFont()->getTextExtentPoint(
				section.str.wptr() + lineIter->first, 
				ctlMin(textlen - lineIter->first, advLineCharNumber),
				lineWidth);
			lineIter->second = lineIter->first + pos;
			if(lineIter->second == textlen)
				break;
			if(lineIter + 1 == sectionData.end())
			{
				sectionData.push_back(_edit_pos(lineIter->second, 0));
				lineIter = sectionData.end() - 1;
			} else ++lineIter;
		}
	}
	ctlEdit::_exactPos ctlEdit::_edit_map_point_to_pos(ctlPoint pt)
	{
		if(EDIT_CHECK_MARCO) return _exactPos(0, 0, 0);
		unsigned lineHeight = getFont()->getFontHeight();
		int firstHeight = _edit_top_offset + getHeightPadding(),
			line = _edit_top_pos.second;
		//计算以当前顶部行为基准的行偏移
		while(firstHeight + getLineLength() / 2.0f <= pt.y)
		{
			firstHeight += getLineLength() + lineHeight;
			++line;
		}
		if(pt.y < 0) ++line;
		while(pt.y < 0 && firstHeight + getLineLength() / 2.0f - pt.y >= lineHeight)
		{
			firstHeight -= getLineLength() + lineHeight;
			--line;
		}
		//寻找在哪一段
		int section = _edit_top_pos.first;
		while(line < 0 && section > 0)
			line += _getLineNumber(--section);
		if(line < 0) line = 0;
		int sectionLine = _getLineNumber(section);
		while(sectionLine <= line && _getSection(section + 1))
		{
			line -= sectionLine;
			sectionLine = _getLineNumber(++section);
		}
		if(line < 0) line = 0;
		if(line >= sectionLine)
			line = sectionLine - 1;
		if(line < 0) return _exactPos(0, 0, 0);
		_textInfo* sectionInst = _getSection(section);
		unsigned lineBegin = sectionInst->wrap[line].first;
		unsigned lineEnd = sectionInst->wrap[line].second;
		auto& txt = sectionInst->str;
		//寻找在哪个字符
		_exactPos ex;
		ex.section = section;
		ex.line = line;
		ex.offset = 0;
		if(pt.x < (int)getLeftPadding())
			return ex;
		ex.offset  =  getFont()->getTextExtentPoint(
			txt.wptr() + lineBegin, lineEnd - lineBegin, 
			pt.x - getLeftPadding());
		ctlPoint txtsize = getFont()->getTextSize(
			txt.wptr() + lineBegin + ex.offset, 1);
		ctlPoint sz = getFont()->getTextSize(
			txt.wptr() + lineBegin, ex.offset);
		if(lineEnd - lineBegin != ex.offset 
			&& txtsize.x / 2 < pt.x - (int)getLeftPadding() - sz.x) 
			++ex.offset;
		return ex;
	}
	ctlPoint ctlEdit::_edit_map_pos_to_point(_exactPos pos)
	{
		if(EDIT_CHECK_MARCO) return ctlPoint(0, 0);
		unsigned lineHeight = getFont()->getFontHeight();
		ctlPoint pt(getLeftPadding(), getHeightPadding() + _edit_top_offset);
		unsigned topsection = _edit_top_pos.first;
		if(pos.section < topsection) return ctlPoint(0, 0);
		//将位置跳到当前段首
		pt.y -= _edit_top_pos.second * (lineHeight + getLineLength()) + lineHeight;
		while(pos.section != topsection)
		{
			unsigned linenum = _getLineNumber(topsection++);
			pt.y += linenum * (lineHeight + getLineLength());
		}
		//增加末尾段位置
		pt.y += pos.line * (lineHeight + getLineLength());
		
		auto section = _getSection(pos.section);
		if(section == nullptr || section->wrap.empty())
			return pt;
		std::wstring::const_pointer text = section->str.wptr() + section->wrap[pos.line].first;
		ctlPoint size = getFont()->getTextSize(text, pos.offset);
		pt.x += size.x;
		return pt;
	}
	ctlEdit::_textInfo* ctlEdit::_getSection(unsigned section)
	{
		if(section >= _edit_str.size())
			return nullptr;
		return &_edit_str[section];
	}
	std::vector<ctlEdit::_edit_pos>* ctlEdit::_getLineInfo(unsigned section)
	{
		_textInfo* sec = _getSection(section);
		if(sec == nullptr) return nullptr;
		return &sec->wrap;
	}
	unsigned ctlEdit::_getLineNumber(unsigned section)
	{
		_textInfo* sec = _getSection(section);
		if(sec == nullptr) return 0;
		return sec->wrap.size();
	}
	bool ctlEdit::create(ctlRoot* parent, ctlRect rect, const ctlText& lpszText,
			const ctlStyleEdit& style)
	{
		if(parent == 0 || isCreated() || !parent->isCreated()) return false; 
		std::lock_guard<decltype(_eventTrans)> gurad(
			_eventTrans.getControlEventTrans(parent));
		_control_virtual_class_end = this;
		_basic_create_(rect, parent);
		_controlType = ctlControlType::ctlEdit;
		_virtual_font_create();
		lockDrawing();

		enableBackground(true);
	
		_exactPos nullPos;
		_edit_caret_pos_begin = _edit_caret_pos_end = nullPos;
		_edit_line_length = 0;
		_edit_scroll_is_callback = false;
		_edit_top_offset = getFont()->getFontHeight();
		_edit_top_pos = _edit_pos(0, 0);
		_edit_access = ctlAccess::readWrite;

		_edit_scroll.create(this, ctlPoint(0, 0), 100);
		_edit_scroll.setMoveFormat(ctlRoot::moveFormat::relative, ctlDirection::xy, this);
		_edit_scroll.setMoveAlignment(ctlRoot::moveAlignment::rightTop);
		_edit_scroll.moveRelative(0, 0);
		_edit_scroll.setSizeFormat(ctlRoot::sizeFormat::percent, ctlDirection::y);
		_edit_scroll.sizePercent(0.0f, 1.0f, ctlDirection::y);

		_edit_scroll.addLambdaEvent(ctlNotifyEventType::scroll, 
			[this](ctlRoot*, ctlNotifyEvent* ne)
		{
			//如果是由 _check_scrollbar 发出的消息则返回
			if(_edit_scroll_is_callback) return;
			ctlNotifyScroll* ns = reinterpret_cast<ctlNotifyScroll*>(ne->info);
			_edit_top_pos = _get_pos_by_abs_pixel(ns->logicLength * ScrollStepPixel);
			unsigned height = _get_abs_pixel_by_pos(_edit_top_pos);
			_edit_top_offset = height - ns->logicLength * ScrollStepPixel;
			if(_edit_top_pos.first == 0 && _edit_top_pos.second == 0)
				_check_scrollbar();
			_flush_caret();
			redraw();
		} );

		setStyle(style);
		setText(lpszText);
		setTextAlignMode(textAlign::left);

		unlockDrawing();
		redraw();
		return true;
	}
	ctlEdit::_exactPos ctlEdit::_insert_text(_exactPos pos, const _ctlInterior::textpack& temptext)
	{
		if(EDIT_CHECK_MARCO) return pos;
		if(!accessWrite() || temptext.length() == 0) return pos;
		std::wstring::const_pointer tabText = L"    ";
		std::wstring tempwstr;
		tempwstr.reserve(temptext.length() + 3);
		for(std::wstring::value_type value : temptext.stdwstr())
		{
			if(value == L'\t')
				tempwstr.append(tabText);
			else tempwstr.push_back(value);
		}
		_ctlInterior::textpack& text = *_ctlInterior::createCompatibleTextPack(&tempwstr);
		_exactPos calcBegin(pos);
		_edit_back_remove.begin = pos;

		std::wstring::const_pointer lineDelim = L"\r\n";
		std::wstring::const_pointer beg = text.wptr();
		std::wstring::const_pointer end = beg + text.length();
		std::wstring::const_pointer next = std::find_first_of(beg, end, lineDelim, lineDelim + 2);
		unsigned firstLen = next - beg;

		for( ; ; )
		{
			_textInfo* section = _getSection(pos.section);
			std::wstring& str = section->str.stdwstr();
			if(pos.line == 0 && pos.offset == 0)
			{
				str.insert(str.begin(), beg, next);
			} else {
				str.insert(str.begin() + section->wrap[pos.line].first + pos.offset, beg, next);
			}
			++pos.section;
			if(next != end) 
				_edit_str.insert(_edit_str.begin() + pos.section, _textInfo());
			else break;
			pos.line = pos.offset = 0;
			if(*next == L'\r' && *(next + 1) == L'\n')
				beg = next + 2;
			else beg = next + 1;
			next = std::find_first_of(beg, end, lineDelim, lineDelim + 2);
		}
		//如果插入新行，移动后半部分元素
		if(pos.section != calcBegin.section + 1)
		{
			_textInfo* section = _getSection(pos.section - 1),
				*oldSection = _getSection(calcBegin.section);
			if(oldSection->wrap.empty() == false)
			{
				unsigned offset = oldSection->wrap[calcBegin.line].first + calcBegin.offset + firstLen;
				section->str.stdwstr().append( 
					oldSection->str.stdwstr().begin() + offset, 
					oldSection->str.stdwstr().end());
				oldSection->str.stdwstr().erase(
					oldSection->str.stdwstr().begin() + offset, 
					oldSection->str.stdwstr().end());
			}
		}

		while(calcBegin.section != pos.section)
		{
			_calc_text_rect(_edit_pos(calcBegin.section, calcBegin.line));
			calcBegin.line = 0;
			++calcBegin.section;
		}
		//修正插入符位置
		unsigned lineend = pos.section - 1;
		_textInfo* section = _getSection(lineend);
		_exactPos eps(lineend, 0, 0);
		if(lineend == _edit_caret_pos_begin.section)
		{
			eps = _edit_caret_pos_begin;
			eps.offset += text.length();
			auto iter = section->wrap.begin() + eps.line;
			while(eps.offset > iter->second - iter->first)
			{
				++eps.line;
				eps.offset -= iter->second - iter->first;
				++iter;
			}
		} else {
			unsigned count = text.length() - text.stdwstr().find_last_of(L"\r\n") - 1;
			for(auto& elem : section->wrap)
			{
				if(count > elem.second - elem.first)
				{
					count -= elem.second - elem.first;
					++eps.line;
				}
			}
			eps.offset = count;
		}
		//记录撤销信息
		_edit_back_remove.end = eps;
		_edit_back_remove.enable = true;
//		_edit_back_insert.enable = false;
	// 不需要，因为之前的步骤有可能是_remove_selected()
		redraw();
		return eps;
	}

	bool ctlEdit::setText(const ctlText& lpszText)
	{
		if(!isCreated() || !isEnabled()) return false;
		_edit_str.clear();
		_textpack.stdwstr().clear();
		_edit_str.push_back(_textInfo());
		_exactPos eps(0, 0, 0);
		_ctlInterior::textpack pack;
		pack.set(lpszText);
		ctlAccess oldAccess = getAccess();
		setAccess(ctlAccess::write);
		_edit_caret_pos_begin = _edit_caret_pos_end = eps;
		_insert_text(eps, pack);
		_send_change_event();
		setAccess(oldAccess);
		_edit_back_insert.enable = false;
		_check_scroll();
		_flush_caret();
		redraw();
		return true;
	}
	void ctlEdit::_position_event_(ctlPositionEvent* pe)
	{
		ctlRoot::_position_event_(pe);
		if(pe->eventType == ctlPositionEventType::size)
			_edit_rect_recalc();
	}
	void ctlEdit::_mouse_event_(ctlMouseEvent* me)
	{
		if(me->eventModel != ctlMouseEventModel::cut) return;
		ctlRoot::_mouse_event_(me);
		if(me->eventType == ctlMouseEventType::move && getAccess() != ctlAccess::none)
			_eventTrans.setCursor(reinterpret_cast<std::wstring::const_pointer>(IDC_IBEAM));
		if(me->eventType == ctlMouseEventType::leftDown)
		{
			_exactPos pos = _edit_map_point_to_pos(me->mousePos);
			_edit_caret_pos_begin = _edit_caret_pos_end = pos;
			_check_scroll();
			auto& caret = _eventTrans.getMajorInfo()->hCaret;
			caret.setColor(static_cast<ctlStyleEdit*>(_styleBase)->caret);
			caret.setLevel(this);
			caret.setLength(getFont()->getFontHeight());
			_flush_caret();
			_capture_mouse_move();
		} else if(me->eventType == ctlMouseEventType::move && isMouseDown() && accessRead()) {
			_edit_caret_pos_end = _edit_map_point_to_pos(me->mousePos);
			_check_scroll();
			_flush_caret();
			redraw();
		} else if(me->eventType == ctlMouseEventType::leftUp) {
			_release_mouse_move();
			if(isPointInControl(me->mousePos) == false)
				_eventTrans.releaseCursor();
		} else if(me->eventType == ctlMouseEventType::leave) {
			_eventTrans.releaseCursor();
		} else if(me->eventType == ctlMouseEventType::wheel) {
			getScroll()->dealMouseWheelEvent(me);
		}
	}
	void ctlEdit::_key_event_(ctlKeyEvent* ke)
	{
		ctlRoot::_key_event_(ke);
		if(ke->eventType == ctlKeyEventType::down)
		{
			auto& caretRef = _eventTrans.getMajorInfo()->hCaret;
			caretRef.setPos(caretRef.getPos());
			if(GetKeyState(VK_CONTROL) & 0xff00)
			{
				std::wstring tempstr;
				switch(ke->charInfo.virtualKey)
				{
				case 'Z':
					{
						decltype(_edit_back_insert) tempBack = _edit_back_insert;
						_edit_back_insert.enable = false;
						if(_edit_back_remove.enable && accessWrite())
						{
							_edit_caret_pos_begin = _edit_back_remove.begin;
							_edit_caret_pos_end = _edit_back_remove.end;
							_remove_selected();
						}
						if(tempBack.enable && accessWrite())
						{
							_exactPos begin = tempBack.begin;
							_edit_caret_pos_end = _insert_text(tempBack.begin, tempBack.pack);
							_edit_caret_pos_begin = begin;
						}
					}
					_send_change_event();			
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case 'A':
					if(accessRead())
					{ 
						unsigned sectionTotal = _edit_str.size() - 1;
						_textInfo* section = _getSection(sectionTotal);
						_edit_caret_pos_begin = _exactPos();
						_edit_caret_pos_end = _exactPos(sectionTotal,
							section->wrap.size() - 1, 
							section->wrap.back().second - section->wrap.back().first);
						_flush_caret();
						redraw();
					}
					break;
				case 'X':
					if(CARET_SELECT && accessRead() && accessWrite())
					{
						tempstr = getSelectedWideString(L"\r\n");
						if(_send_edit_event(tempstr, ctlNotifyEdit::cut))
						{
							clipboard_copy(tempstr);
							_remove_selected();
							_send_change_event();
							_check_scroll();
							_flush_caret();
							redraw();
						}
					}
					break;
				case 'C':
					if(CARET_SELECT && accessRead())
					{
						tempstr = getSelectedWideString(L"\r\n");
						if(_send_edit_event(tempstr, ctlNotifyEdit::copy))
							clipboard_copy(tempstr);
					}
					break;
				case 'V': 
					if(accessWrite())
					{
						_ctlInterior::textpack pack;
						clipboard_get(pack);
						if(pack.stdwstr().empty() == false &&
							_send_edit_event(pack.stdwstr(), ctlNotifyEdit::paste))
						{
							_remove_selected();
							_exactPos eps = _insert_text(_edit_caret_pos_begin, pack);
							_edit_caret_pos_begin = _edit_caret_pos_end = eps;
							_send_change_event();
							_check_scroll();
							_flush_caret();
							redraw();
						}
					}
					break;
				case VK_RIGHT:
					{
						_exactPos eps = _edit_caret_pos_end;
						//获取节
						_textInfo* section = _getSection(eps.section);
						unsigned textpos = section->wrap[eps.line].first + eps.offset,
							textsize = section->wrap.back().second;
						//如果插入符在行末尾
						if(textpos == textsize)
						{
							_edit_caret_pos_end = _get_next_pos(eps);
						} else {
							//忽略空白符
							auto begin = section->str.stdwstr().begin(), 
								end = section->str.stdwstr().end(), 
								iter = begin + textpos;
							std::wstring::value_type begin_char = *iter;
							while(iter != end && ctlIsBlank(*iter)) ++iter;
							//查找连续字符
							while(iter != end && ctlIsAlnum(*iter)) ++iter;
							_edit_caret_pos_end = _get_exact_pos_by_section_ans_offset(eps.section, iter - begin);
							if(!ctlIsAlnum(begin_char) && !ctlIsAlnum(begin_char))
								_edit_caret_pos_end = _get_next_pos(_edit_caret_pos_end);
						}
					}
					if((GetKeyState(VK_SHIFT) & 0xff00) == 0 || !accessRead())
						_edit_caret_pos_begin = _edit_caret_pos_end;
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case VK_LEFT:
					{
						_exactPos eps = _edit_caret_pos_end;
						//获取节
						_textInfo* section = _getSection(eps.section);
						unsigned textpos = section->wrap[eps.line].first + eps.offset,
							textsize = section->wrap.back().second;
						//如果插入符在行开头
						if(textpos == 0)
						{
							_edit_caret_pos_end = _get_previous_pos(eps);
						} else {
							//忽略空白符
							auto begin = section->str.stdwstr().begin(),
								iter = begin + textpos - 1;
							int begin_char = static_cast<int>(*iter);
							while(iter != begin && ctlIsBlank(*iter)) --iter;
							//查找连续字符
							while(iter != begin && ctlIsAlnum(*iter)) --iter;
							_edit_caret_pos_end = _get_exact_pos_by_section_ans_offset(eps.section,
								iter - begin == 0 ? 0 : iter - begin + 1);
							if(!ctlIsBlank(begin_char) && !ctlIsAlnum(begin_char))
								_edit_caret_pos_end = _get_previous_pos(_edit_caret_pos_end);
						}
					}
					if((GetKeyState(VK_SHIFT) & 0xff00) == 0 || !accessRead())
						_edit_caret_pos_begin = _edit_caret_pos_end;
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				}
			} else if(GetKeyState(VK_SHIFT) & 0xff00) {
				ctlPoint pt;
				if(accessRead())
				{
					switch(ke->charInfo.virtualKey)
					{
					case VK_LEFT:
						_edit_caret_pos_end = _get_previous_pos(_edit_caret_pos_end);
						_check_scroll();
						_flush_caret();
						redraw();
						break;
					case VK_RIGHT:
						_edit_caret_pos_end = _get_next_pos(_edit_caret_pos_end);
						_check_scroll();
						_flush_caret();
						redraw();
						break;
					case VK_PRIOR:
						_caret_move(-(getHeight() + (int)getHeightPadding() * 2), false);
						break;
					case VK_NEXT:
						_caret_move(getHeight() + (int)getHeightPadding() * 2, false);
						break;
					case VK_UP:
						_caret_move(-getLineLength() - (int)getFont()->getFontHeight(), false);
						break;
					case VK_DOWN:
						_caret_move(getLineLength() + getFont()->getFontHeight(), false);
						break;
					}
				}
			} else {
				ctlPoint pt, wid;
				switch(ke->charInfo.virtualKey)
				{
				case VK_PRIOR:
					_caret_move(-(getHeight() + (int)getHeightPadding() * 2), true);
					break;
				case VK_NEXT:
					_caret_move(getHeight() + (int)getHeightPadding() * 2, true);
					break;
				case VK_UP:
					_caret_move(-getLineLength() - (int)getFont()->getFontHeight(), true);
					break;
				case VK_DOWN:
					_caret_move(getLineLength() + getFont()->getFontHeight(), true);
					break;
				case VK_LEFT:
					if(CARET_SELECT)
					{
						if(CARET_COMP(<))
							_edit_caret_pos_end = _edit_caret_pos_begin;
						else _edit_caret_pos_begin = _edit_caret_pos_end;
					} else {
						_edit_caret_pos_begin = _edit_caret_pos_end = 
							_get_previous_pos(_edit_caret_pos_begin);
					}
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case VK_RIGHT:
					if(CARET_SELECT)
					{
						if(CARET_COMP(>))
							_edit_caret_pos_end = _edit_caret_pos_begin;
						else _edit_caret_pos_begin = _edit_caret_pos_end;
					} else {
						_edit_caret_pos_begin = _edit_caret_pos_end = 
							_get_next_pos(_edit_caret_pos_begin);
					}
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case VK_BACK:
					if(accessWrite())
					{
						if(CARET_SELECT == false)
							_edit_caret_pos_begin = _get_previous_pos(_edit_caret_pos_begin);
						_remove_selected();
						_send_change_event();
						_check_scroll();
						_flush_caret();
						redraw();
					}
					break;
				case VK_DELETE:
					if(accessWrite())
					{
						if(CARET_SELECT == false)
							_edit_caret_pos_begin = _get_next_pos(_edit_caret_pos_begin);
						_remove_selected();
						_send_change_event();
						_check_scroll();
						_flush_caret();
						redraw();
					}
					break;
				}
			}
		} else if(ke->eventType == ctlKeyEventType::input && accessWrite()) {
			//判断字符是否可打印
			std::wstring::value_type text[2] = { ke->charInfo.wtext, 0 };
			if(!_eventTrans.getMajorInfo()->isUnicode)
				MultiByteToWideChar(CP_ACP, 0, ke->charInfo.text, -1, text, 1);
			if(!(*text < 0x20 && *text != '\n' && *text != '\t' && *text != '\r' || *text == 0x7f))
			{
				//插入
				auto section = _getSection(_edit_caret_pos_begin.section);
				unsigned pos = section->wrap[_edit_caret_pos_begin.line].first + _edit_caret_pos_begin.offset;
				ctlNotifyEvent ne;
				ctlNotifyEdit nee;
				ne.eventType = ctlNotifyEventType::edit;
				ne.info = &nee;
				nee.state = ctlNotifyEdit::insert;
				bool allow = true;
				nee.isAllow = &allow;
				nee.str = nullptr;
				if(_eventTrans.getMajorInfo()->isUnicode)
					nee.text.wc = ke->charInfo.wtext;
				else memcpy(nee.text.c, ke->charInfo.text, 3);
				_notify_event_(&ne);
				if(allow)
				{
					_remove_selected();
					_ctlInterior::textpack pack;
					pack.set(text);
					_edit_caret_pos_begin = _edit_caret_pos_end = 
						_insert_text(_edit_caret_pos_begin, pack);
					_send_change_event();
					_check_scroll();
					_flush_caret();
					redraw();
				}
			}
		}
	}
	void ctlEdit::_flush_caret()
	{
		auto& caret = _eventTrans.getMajorInfo()->hCaret;
		if(getAccess() == ctlAccess::none)
		{
			caret.enable(false);
			return;
		}
		if(CARET_SELECT)
		{
			caret.enable(false);
		} else {
			caret.enable(true);
			caret.setPos(_edit_map_pos_to_point(_edit_caret_pos_begin));
		}
	}
	ctlEdit::_exactPos ctlEdit::_get_previous_pos(_exactPos pos)
	{
		_check_pos(pos);
		if(pos.offset == 0)
		{
			if(pos.line == 0)
			{
				if(pos.section != 0)
				{
					--pos.section;
					pos.line = _getLineNumber(pos.section) - 1;
					auto wrap = _getSection(pos.section)->wrap.back();
					pos.offset = wrap.second - wrap.first;
				}
			} else {
				--pos.line;
				auto wrap = _getSection(pos.section)->wrap[pos.line];
				pos.offset = wrap.second - wrap.first - 1;
			}
		} else --pos.offset;
		return pos;
	}
	ctlEdit::_exactPos ctlEdit::_get_next_pos(_exactPos pos)
	{		
		_check_pos(pos);
		auto line = _getSection(pos.section)->wrap[pos.line];
		if(pos.offset + 1 >= line.second - line.first)
		{
			//如果是最后一行
			if(pos.line + 1 == _getLineNumber(pos.section))
			{
				//如果插入符已经在末尾
				if(pos.offset == line.second - line.first)
				{
					if(_edit_str.size() != pos.section + 1)
					{
						++pos.section;
						pos.line = pos.offset = 0;
					}
				//插入符在倒数第二个位置，不在末尾
				} else ++pos.offset;
			//不是最后一行，但是插入符在倒数第二个位置
			//说明遇到自动换行，直接跳到下一行
			} else {
				++pos.line;
				pos.offset = pos.offset == line.second - line.first ? 1 : 0;
			}
		} else ++pos.offset;
		return pos;
	}
	void ctlEdit::_remove_selected()
	{
		if(EDIT_CHECK_MARCO) return;
		if(!accessWrite()) return;
		//获取较小的一个位置
		_exactPos begin = _edit_caret_pos_begin,
			end = _edit_caret_pos_end;
		if(CARET_COMP_V(begin, end, >))
			std::swap(begin, end);
		//记录撤销信息
		_edit_back_insert.pack.set(getSelectedWideString());
		_edit_back_insert.begin = begin;
		_edit_back_insert.end = end;
		_edit_back_insert.enable = true;
		_edit_back_remove.enable = false;
	
		auto section = _getSection(begin.section);
		unsigned begpos = section->wrap[begin.line].first + begin.offset;
		if(begin.section == end.section)
		{
			auto& stdstr = section->str.stdwstr();
			stdstr.erase(stdstr.begin() + begpos,
				stdstr.begin() + section->wrap[end.line].first + end.offset);
			_edit_caret_pos_begin = _edit_caret_pos_end = begin;
			_calc_text_rect(_edit_pos(begin.section, begin.line));
			redraw();
			return;
		}
		//合并开头结尾
		auto stdstr = &section->str.stdwstr();
		stdstr->erase(stdstr->begin() + begpos, stdstr->end());
		auto nextsection = _getSection(end.section);
		auto nextstr = &nextsection->str.stdwstr();
		nextstr->erase(nextstr->begin(), 
		nextstr->begin() + nextsection->wrap[end.line].first + end.offset);
		stdstr->append(nextstr->begin(), nextstr->end());
		_edit_str.erase(_edit_str.begin() + end.section);
		//移除中间段落
		_edit_str.erase(_edit_str.begin() + begin.section + 1, 
			_edit_str.begin() + end.section);
		//重新计算位置信息
		_edit_caret_pos_begin = _edit_caret_pos_end = begin;
		_calc_text_rect(_edit_pos(begin.section, begin.line));
		_check_scroll();
	}
	void ctlEdit::_send_change_event()
	{
		ctlNotifyEvent ne;
		ne.info = nullptr;
		ne.eventType = ctlNotifyEventType::editChange;
		_notify_event_(&ne);
	}
	void ctlEdit::_check_pos(_exactPos& pos)
	{
		if(pos.section >= _edit_str.size())
		{
			pos.section = _edit_str.size() - 1;
			pos.line = _getLineNumber(pos.section);
			pos.offset = _getSection(pos.section)->wrap.back().second;
		} else {
			if(pos.line >= _getLineNumber(pos.section))
			{
				pos.line -= _getLineNumber(pos.section);
				++pos.section;
				_check_pos(pos);
			} else {
				auto pr = _getSection(pos.section)->wrap[pos.line];
				if(pos.offset > pr.second)
				{
					pos.offset -= pr.second - pr.first;
					++pos.line;
					_check_pos(pos);
				}
			}
		}
	}
	void ctlEdit::_check_scroll()
	{
		_edit_pos pos(_edit_caret_pos_end.section, _edit_caret_pos_end.line);
		if(pos.first < _edit_top_pos.first
			|| pos.first == _edit_top_pos.first 
			&& pos.second <= _edit_top_pos.second)
		{
			_edit_top_offset = getFont()->getFontHeight();
			_edit_top_pos = pos;
		}
		
		//检测下半部分
		_exactPos eps = _edit_caret_pos_end;
		int lineHeight = getFont()->getFontHeight();
		ctlPoint caretPoint = _edit_map_pos_to_point(eps);
		int d2 = getHeight() - getHeightPadding() - lineHeight;
		if(caretPoint.y <= d2)
		{
			_check_scrollbar();
			return;
		}
		int moveLen = caretPoint.y - d2;

		int line = _edit_top_pos.second;
		int section = _edit_top_pos.first;
		while(moveLen >= (int)_edit_top_offset)
		{
			moveLen -= lineHeight + getLineLength();
			if(++line == _getLineNumber(section))
			{
				line = 0;
				++section;
			}
		}
		_edit_top_offset -= moveLen;
		_edit_top_pos = _edit_pos(section, line);
		_check_scrollbar();
	}
	void ctlEdit::_check_scrollbar()
	{
		unsigned realHeight = getHeight() - getHeightPadding() * 2;
		unsigned scrollHeight = _get_abs_pixel_by_pos(_edit_top_pos) - _edit_top_offset;
		unsigned totalHeight = ctlMax(scrollHeight + realHeight, getTotalHeight());
		if(totalHeight <= realHeight)
		{
			if(getScroll()->isEnabled())
			{
				getScroll()->hide();
				getScroll()->enable(false, true);
				_edit_rect_recalc();
				return;
			}
		} else {
			if(!getScroll()->isEnabled())
			{
				getScroll()->show();
				getScroll()->enable(true, true);
				_edit_rect_recalc();
				return;
			}
			_edit_scroll_is_callback = true;
			getScroll()->setMaxLogicLength((totalHeight - realHeight) / ScrollStepPixel);
			getScroll()->setScrolledLogicLength(scrollHeight / ScrollStepPixel);
			_edit_scroll_is_callback = false;
		}
	}
	std::wstring ctlEdit::getSelectedWideString(std::wstring::const_pointer lineDelim)
	{
		if(CARET_SELECT)
		{
			_exactPos begin = _edit_caret_pos_begin,
				end = _edit_caret_pos_end;
			if(CARET_COMP_V(begin, end, >))
				std::swap(begin, end);
			_textInfo* section = _getSection(begin.section);
			std::wstring::const_pointer txt = section->str.wptr();
			if(begin.section == end.section)
			{
				return std::wstring(txt + section->wrap[begin.line].first + begin.offset, 
					txt + section->wrap[end.line].first + end.offset);
			}
			std::wstring wstr(txt + section->wrap[begin.line].first 
				+ begin.offset, txt + section->wrap.back().second);
			while(++begin.section != end.section)
			{
				wstr.append(lineDelim);
				wstr += _getSection(begin.section)->str.stdwstr();
			}
			section = _getSection(begin.section);
			txt = section->str.wptr();
			wstr.append(lineDelim);
			wstr.append(txt, txt + section->wrap[end.line].first + end.offset);
			return std::move(wstr);
		}
		return std::wstring();
	}
	std::string ctlEdit::getSelectedString(std::string::const_pointer lineDelim)
	{
		_ctlInterior::textpack pack;
		pack.set(lineDelim);
		std::wstring ws = getSelectedWideString(pack.wptr());
		unsigned len;
		std::string str(len = WideCharToMultiByte(CP_OEMCP, 0, ws.c_str(), -1, 0, 0, 0, 0), 0);
		WideCharToMultiByte(CP_OEMCP, 0, ws.c_str(), -1, &str[0], len, 0, 0);
		return std::move(str);
	}
	bool ctlEdit::_send_edit_event(std::wstring& str, decltype(ctlNotifyEdit::copy) state)
	{
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::edit;
		ctlNotifyEdit nee;
		ne.info = &nee;
		bool allow = true;
		nee.isAllow = &allow;
		nee.state = state;
		nee.str = &str;
		nee.text.wc = 0;
		_notify_event_(&ne);
		return allow;
	}
	unsigned ctlEdit::getTextLength()
	{
		unsigned len = 1;
		for(auto& elem : _edit_str)
			len += elem.str.length() + 1;
		return --len;
	}
	unsigned ctlEdit::getANSITextSize()
	{
		unsigned len = 1;
		for(auto& elem : _edit_str)
			len += elem.str.ansiLength() + 1;
		return --len;
	}
	namespace 
	{
		template<typename Type, typename EditStr, typename Func>
		unsigned ___getText(ctlEdit* root, EditStr& _edit_str, Func func, Type lpszText, unsigned bufferLen)
		{
			if(lpszText == nullptr) return 0;
			unsigned cpyLen = 0;
			if(bufferLen == 0)
				bufferLen = (root->*func)() + 1;
			for(auto& elem : _edit_str)
			{
				cpyLen += elem.str.get(lpszText + cpyLen, bufferLen - cpyLen);
				lpszText[cpyLen - 1] = '\n';
			}
			lpszText[cpyLen - 1] = 0;
			return cpyLen;
		}
	}
	unsigned ctlEdit::getText(char* lpszText, unsigned bufferLen)
	{
		return ___getText(this, _edit_str, &ctlEdit::getTextLength, lpszText, bufferLen);
	}
	unsigned ctlEdit::getText(std::wstring::pointer lpszText, unsigned bufferLen)
	{
		return ___getText(this, _edit_str, &ctlEdit::getANSITextSize, lpszText, bufferLen);
	}
	std::wstring ctlEdit::getWideText()
	{
		unsigned len = getTextLength() + 1;
		std::wstring wstr(len, 0);
		getText(&wstr[0], len);
		return std::move(wstr);
	}
	std::string ctlEdit::getText()
	{
		unsigned len = getANSITextSize() + 1;
		std::string wstr(len, 0);
		getText(&wstr[0], len);
		return std::move(wstr);
	}
	unsigned ctlEdit::getLineNumber()
	{
		unsigned total = 0;
		for(_textInfo& elem : _edit_str)
			total += elem.wrap.size();
		return total;
	}
	unsigned ctlEdit::_get_abs_line(_edit_pos pos)
	{
		unsigned line = pos.second, count = 0;
		for(_textInfo& elem :  _edit_str)
		{
			if(count++ == pos.first)
				break;
			line += elem.wrap.size();
		}
		return line + 1;
	}
	ctlEdit::_edit_pos ctlEdit::_get_pos_by_abs_pixel(unsigned pixel)
	{
		unsigned section = 0, sectionHeight = 0;
		unsigned lineHeight = getFont()->getFontHeight();
		while(_getSection(section))
		{
			sectionHeight += _getLineNumber(section) * (lineHeight + getLineLength());
			if(pixel > sectionHeight)
				++section;
			else return _edit_pos(section, _getLineNumber(section) - 1 - (
				(sectionHeight - ctlMax(1, pixel)) / (lineHeight + getLineLength())));
		}
		--section;
		return _edit_pos(section, _getLineNumber(section) - 1);
	}
	unsigned ctlEdit::_get_abs_pixel_by_pos(_edit_pos pos)
	{
		unsigned lineHeight = getFont()->getFontHeight();
		return _get_abs_line(pos) * (lineHeight + getLineLength()) - getLineLength();
	}
	void ctlEdit::_edit_rect_recalc()
	{
		//保存当前位置信息
		std::vector<_exactPos*> eps;
		eps.push_back(&_edit_caret_pos_begin);
		eps.push_back(&_edit_caret_pos_end);
		if(_edit_back_insert.enable)
			eps.push_back(&_edit_back_insert.begin);
		if(_edit_back_remove.enable)
		{
			eps.push_back(&_edit_back_remove.begin);
			eps.push_back(&_edit_back_remove.end);
		}
		_edit_pos absPos[5];
		for(unsigned i = 0; i < eps.size(); ++i)
		{
			if(_getSection(eps[i]->section)->wrap.empty())
				absPos[i] = _edit_pos(0, 0);
			else absPos[i] = _edit_pos(eps[i]->section, 
				_getSection(eps[i]->section)->wrap[eps[i]->line].first + eps[i]->offset);
		}
		for(unsigned i = 0; i < _edit_str.size(); ++i)
			_calc_text_rect(_edit_pos(i, 0));
		//转换位置到新位置
		for(unsigned i = 0; i < eps.size(); ++i)
		{
			*eps[i] = _get_exact_pos_by_section_ans_offset(
				absPos[i].first, absPos[i].second);
		}
		_check_scroll();
		_flush_caret();
	}
	void ctlEdit::_caret_move(int distance, bool isAllChange)
	{
		ctlPoint pt = _edit_map_pos_to_point(_edit_caret_pos_end);
		pt.y += distance;
		_edit_caret_pos_end = _edit_map_point_to_pos(pt);
		if(isAllChange) _edit_caret_pos_begin = _edit_caret_pos_end;
		_check_scroll();
		_flush_caret();
		redraw();
	}
	ctlEdit::_exactPos ctlEdit::_get_exact_pos_by_section_ans_offset
		(unsigned section, unsigned offset)
	{
		unsigned line = 0;
		for(auto pr : _getSection(section)->wrap)
		{
			if(offset == pr.second)
			{
				if(pr.second == _getSection(section)->wrap.back().second)
					offset = pr.second - pr.first;
				else ++line, offset = 0;
				 break;
			} else if(offset < pr.second) {
				offset -= pr.first;
				break;
			} else ++line;
		}
		return _exactPos(section, line, offset);
	}
}